package atomic

import (
	"fmt"
	"sync/atomic"
	"time"
)

/*
sync/atomic包中提供了几种原子操作？可操作的数据类型又有哪些？
	sync/atomic包中的函数可以做的原子操作有：
		加法（add）
		比较并交换（compare and swap，简称 CAS）
		加载（load）
		存储（store）
		交换（swap）

	传入这些原子操作函数的第一个参数值对应的都应该是那个被操作的值。比如，atomic.AddInt32函数的第一个参数，对应的一定是那个要被增大的整数。
	可是，这个参数的类型为什么不是int32而是*int32呢？

		因为原子操作函数需要的是被操作值的指针，而不是这个值本身；
		被传入函数的参数值都会被复制，像这种基本类型的值一旦被传入函数，就已经与函数外的那个值毫无关系了。

	用于原子加法操作的函数可以做原子减法吗？
		当然是可以的。atomic.AddInt32函数的第二个参数代表差量，它的类型是int32，是有符号的。
		如果想做原子减法，那么把这个差量设置为负整数就可以了。

	比较并交换操作与交换操作相比有什么不同？优势在哪里？

		比较并交换操作即 CAS 操作，是有条件的交换操作，只有在条件满足的情况下才会进行值的交换。
		所谓的交换指的是，把新值赋给变量，并返回变量的旧值。

	假设已经保证了对一个变量的写操作都是原子操作，比如：加或减、存储、交换等等，那对它进行读操作的时候，还有必要使用原子操作吗？

		很有必要。其中的道理可以对照一下读写锁。
		为什么在读写锁保护下的写操作和读操作之间是互斥的？这是为了防止读操作读到没有被修改完的值。

问题：怎样用好sync/atomic.Value？
	atomic.Value类型是开箱即用的，声明一个该类型的变量（以下简称原子变量）之后就可以直接使用了。
	这个类型使用起来很简单，它只有两个指针方法：Store和Load。

	1.首先一点，一旦atomic.Value类型的值（以下简称原子值）被真正使用，它就不应该再被复制了。
	2.只要用它来存储值了，就相当于开始真正使用了。atomic.Value类型属于结构体类型，而结构体类型属于值类型。
	3.复制该类型的值会产生一个完全分离的新值。这个新值相当于被复制的那个值的一个快照。之后，不论后者存储的值怎样改变，都不会影响到前者，反之亦然。

	用原子值来存储值，有两条强制性的使用规则:
		1.第一条规则，不能用原子值存储nil。
			不能把nil作为参数值传入原子值的Store方法，否则就会引发一个 panic。

		2.第二条规则，向原子值存储的第一个值，决定了它今后能且只能存储哪一个类型的值。
			第一次向一个原子值存储了一个string类型的值，那在后面就只能用该原子值来存储字符串了。
			如果又想用它存储结构体，那么在调用它的Store方法的时候就会引发一个panic。

	几条具体的使用建议:
		1.不要把内部使用的原子值暴露给外界。比如，声明一个全局的原子变量并不是一个正确的做法。这个变量的访问权限最起码也应该是包级私有的。
		2.如果不得不让包外，或模块外的代码使用你的原子值，那么可以声明一个包级私有的原子变量，然后再通过一个或多个公开的函数，让外界间接地使用到它。
		注意，这种情况下不要把原子值传递到外界，不论是传递原子值本身还是它的指针值。
		3.如果通过某个函数可以向内部的原子值存储值的话，那么就应该在这个函数中先判断被存储值类型的合法性。
		若不合法，则应该直接返回对应的错误值，从而避免 panic 的发生。
		4.如果可能的话，可以把原子值封装到一个数据类型中，比如一个结构体类型。
		这样，既可以通过该类型的方法更加安全地存储值，又可以在该类型中包含可存储值的合法类型信息。



*/

var num int32 = 10

func main() {

	// 使用 CAS与for语句联用就可以实现一种简易的自旋锁（spinlock）
	for {
		if atomic.CompareAndSwapInt32(&num, 10, 10) {
			fmt.Println("The second number has gone to zero.")
			break
		}
		time.Sleep(time.Millisecond * 100)
	}

}
